//
// Omnima Limited (c) 2008-2011 All Rights Reserved
//

#include "serialio.h"
#include "olcd.h"

#ifdef COMPILE_FOR_WINDOWS
#pragma warning(disable:4996)
#endif

//------------------------------------------------------------------------------------
//- Utility functions
//------------------------------------------------------------------------------------

char sHexDecToNum(const char ch)
//Covert single digit character to numerical value
{
    if (ch>='0' && ch<='9')
        return ch-'0';

    if (ch>='A' && ch<='F')
        return ch-'A'+10;

    if (ch>='a' && ch<='f')
        return ch-'a'+10;

    return -1;
}

const char* sStrStr(const char *in, const char *str)
//Find position of str within in, or return 0
{
    char c;
    unsigned int len;

    c = *str++;
    if (!c)
        return (char *) in; // Trivial empty string case

    len = strlen(str);
    do {
        char sc;

        do {
            sc = *in++;
            if (!sc)
                return (char *) 0;
        } while (sc != c);
    } while (strncmp(in, str, len) != 0);

    return (char *) (in - 1);
}

unsigned int HextoDec(const char *str, unsigned int *pResVal)
{
    unsigned int i,d,cn;
    
    for(cn=0,i=0;str[i]!=0;i++)
    {    
        if (str[i]==' ') continue;
        d=sHexDecToNum(str[i]);
        if (d==-1) continue;
        
        *pResVal=*pResVal*16+d;
        cn++;
    }
    return cn;
}

unsigned int g_silenceTimeoutMs=500;
unsigned int g_totalTimeoutMs=1000;
unsigned char g_printResponses=0;

void OmnimaAPISetPrintResponses(unsigned char b)
{
    g_printResponses=b;
}

void OmnimaAPISetSerialSilenceTimeoutDefault()
{
    g_silenceTimeoutMs=500;
}

void OmnimaAPISetSerialSilenceTimeout(unsigned int sMs)
{
    g_silenceTimeoutMs=sMs;
}

void OmnimaAPISetSerialTotalTimeoutDefault()
{
    g_totalTimeoutMs=1000;
}

void OmnimaAPISetSerialTotalTimeout(unsigned int tMs)
{
    g_totalTimeoutMs=tMs;
}

//------------------------------------------------------------------------------------

unsigned int DetectOLCDComPort(char **ppport, char **ppbaud)
//Tries to detect active OLCD COM port
//
//ppport - specifies COM port to use, or if NULL this function tries to detect port
//ppbaud - specifies COM baud to use, or if NULL this function tries to detect speed
//
//Returns 1 if successful, having set ppport and ppbaud to valid/detected port values
//Returns 0 if it fails
{
    unsigned int p, b, detected=0;

    char *baudToTry[] = {"1152000", "576000", "230400", "115200", "19200", "9600", 0};

#ifdef __APPLE__
    char *portToTest[] = {"/dev/tty.usbserial", "/dev/cu.PL2303-002034FD","/dev/tty.PL2303-002034FD", NULL};
#endif

#ifdef COMPILE_FOR_LINUX 
#ifndef __APPLE__
    char *portToTest[] = {"/dev/ttyUSB0", "/dev/ttyUSB1","/dev/ttyUSB2","/dev/ttyUSB3","/dev/ttyUSB4","/dev/ttyS0","/dev/ttyS1",NULL};
#endif
#endif

#ifdef COMPILE_FOR_WINDOWS
    char *portToTest[] = {"COM1","COM2","COM3","COM4","COM5","COM6","COM7","COM8","COM9","COM10",
        "COM11","COM12","COM13","COM14","COM15","COM16","COM17","COM18","COM19","COM20",NULL};
#endif

    //If port specified, try to detect active baud
    if (*ppport!=0)
    {
        for(b=0;baudToTry[b]!=NULL;b++)
        {
            DebugPrintf(1, "%s@%s: ", *ppport, baudToTry[b]); 
            detected=OmnimaAPITestPort(*ppport, baudToTry[b]);
            if (detected) 
            {
                DebugPrintf(1, "Detected: %s@%s\n", *ppport, baudToTry[b]);
                *ppbaud=baudToTry[b];
                return 1;
            }
        }
        return 0;
    }

    //Discover port and baud
    for(p=0;portToTest[p]!=NULL;p++)
    {
        for(b=0;baudToTry[b]!=NULL;b++)
        {
            DebugPrintf(1, "%s@%s: ", portToTest[p], baudToTry[b]); 
            detected=OmnimaAPITestPort(portToTest[p], baudToTry[b]);
            if (detected) 
            {
                DebugPrintf(1, "Detected: %s@%s\n", portToTest[p], baudToTry[b]);
                *ppport=portToTest[p];
                *ppbaud=baudToTry[b];
                return 1;
            }
        }
    }
    return 0;
}

//------------------------------------------------------------------------------------

unsigned int ProcessCmdValueResponse(char *cmdResp, unsigned int buffLen,
                                      unsigned int *pResVal)
{
    char *pDot=cmdResp;
    unsigned int cn=0;
    
    *pResVal=0;
    
    //Look for the leading #!
    pDot=strstr(cmdResp, "#!");
    if (pDot)
    {
        cn=HextoDec(pDot, pResVal);
        //DebugPrintf(1, "Received value: %d(0x%x)\n", *pResVal, *pResVal);
    }
    return cn;
}

typedef struct 
{
    char *str;
    unsigned int len;
} respType;

void respAdd(respType *pResp, const char *str)
{
    char *newStr;
    unsigned int newLen, newStrLen;
    
    if (str)
    {
        newStrLen=strlen(str);
        newLen=pResp->len+newStrLen+1;
        if (pResp->str==0)
        {
            newStr=malloc(newLen);
        } else
        {
            newStr=realloc(pResp->str, newLen);
        }
        if (newStr)
        {
            strcpy(&newStr[pResp->len], str);
            pResp->len+=newStrLen;
            pResp->str=newStr;
        }
    } else
    {
        free(pResp->str);
        pResp->str=0;
    }
}

void WaitForSerialResponses(const termioType *pio, 
                            char **pBuff, 
                            const char *termStr,
                            unsigned int retryMs, 
                            unsigned int timeoutMs,
							unsigned char bPrintResponses)
//Buffers responses from the serial port over a period of time
//
//pio - serial port info
//pBuff - variable to receive the result of this call
//termStr - string specifying termination str. if received the wait it over.
//retryMs - total time allowed to wait or 0 - unlimited waitForResp
//timeoutMs - number of Ms before the call times out
{
    //const char *omnimaApiResponse="OLCD:";
    unsigned int i;
    respType resp;
    unsigned int silenceMs=0, totalWaitMs=0, realsize;
    
    char AnswerBuffer[1024];
    const unsigned int AnswerLength = sizeof(AnswerBuffer);
    
    resp.str=0;
    resp.len=0;
    
    DebugPrintf( 4, "WSR(%s):", termStr?termStr:"");
    
    for(i=0;;i++)
    {
        OReceiveComPort(pio, AnswerBuffer, AnswerLength - 1, &realsize, 0, 100);
        if (realsize>0)
        {
            //Bytes received
            silenceMs=0;
            
            respAdd(&resp, AnswerBuffer);
            
            DebugPrintf( bPrintResponses?1:4, "%s", AnswerBuffer);
            
            if (termStr && strstr(resp.str, termStr)) break;
        } else
        {
            //No bytes, the OS is probably caching the data so wait a bit
            silenceMs+=100;
            totalWaitMs+=100;
        }
        
        if (silenceMs>timeoutMs) 
        {
            DebugPrintf( 4, "- silence");
            break;
        }
        if (retryMs>0 && totalWaitMs>=retryMs)
        {
            DebugPrintf( 4, "- timeout");
            break;
        }
    }
    
    DebugPrintf( bPrintResponses?1:4, "\n");
    *pBuff=resp.str;
}

unsigned int OmnimaAPISendCmdArp2(const termioType *pio,
                     int waitForResp,
                     unsigned int *pRespVal, 
					 const char *cmd,
                     va_list arp)
//Sends command to LCD and waits optionally for resposnses (va_list version)
{
    char *answerStr=0;
    unsigned int retry, retVal=0;
    char *omnimaApiResponse="OLCD:";
	char strCmdFmt[1024];
    const char *pResp;
		
	//DebugPrintf( 1, "OmnimaAPISendCmdArp2 Fmt: '%s', %x, %c\n", cmd, cmd, cmd[0]);
	vsnprintf(strCmdFmt, sizeof(strCmdFmt), cmd, arp);   
	//DebugPrintf( 1, "OmnimaAPISendCmdArp2 Cmd: '%s'\n", strCmdFmt);
	
    for(retry=0;retry<10;retry++)
    {
        OClearSerialPortBuffers(pio);
        if (strlen(strCmdFmt)>0) OSendComPort(pio, strCmdFmt);
        
        if (!waitForResp)
        {
            //DebugPrintf( 1, "Not waiting for response\n", cmd);
            return 0;
        }
        
        answerStr=0;
        WaitForSerialResponses(pio, &answerStr, pRespVal?"\n":omnimaApiResponse, 
                               g_totalTimeoutMs, g_silenceTimeoutMs, g_printResponses);
        if (!answerStr) continue;
                
        if (pRespVal) 
        {
            retVal=ProcessCmdValueResponse(answerStr, strlen(answerStr), pRespVal);
            free(answerStr);
            return retVal;
        }
        
        //DebugPrintf( 1, "OmnimaAPISendCmdArp cmd: '%s' and '%s'\n", answerStr, omnimaApiResponse);
        
        pResp=sStrStr(answerStr, omnimaApiResponse);
        if (pResp!=NULL)
        {
            free(answerStr);
            return 1;
        } else
        {
            //DebugPrintf( 2, "No response received\n");
        }
        free(answerStr);
    }
    return 0;
}

unsigned int OmnimaAPISendCmd(const termioType *pio, 
                     int waitForResp,
                     unsigned int *pRespVal,
                     const char *cmd,
                     ...)
//Sends command to LCD and waits optionally for resposnses (... version)
{
    unsigned int retVal;

    va_list arp;
    va_start(arp, cmd);
    retVal=OmnimaAPISendCmdArp2(pio, waitForResp, pRespVal, cmd, arp);
    va_end(arp);
    return retVal;  
}

unsigned int OmnimaAPIWaitForPrompt(const termioType *pio, unsigned char bAskForPrompt)
//Waits for LCD to respond with the OLCD prompt
{
    unsigned int i;
    const unsigned int retryCount=2;
    
    DebugPrintf(4, "Waiting for OLCD prompt\n");
    for(i=0;!OmnimaAPISendCmd(pio, 1, 0, bAskForPrompt?"\n\r":"") && i<retryCount;i++)
    {
        DebugPrintf(1, ".");
        Sleep(100);
    }
    DebugPrintf(1, "\n");
    if (i>=retryCount) return 0; else return 1;
}


unsigned int OmnimaAPISendCmdWithSync(const termioType *pio,  
                     int waitForResp,
                     unsigned int *pRespVal,
                     const char *cmd,
                     ...)
//Send command to LCD with prompt sync
{
    unsigned int retVal=0;

    va_list arp;	
	va_start(arp, cmd);
    
    if (OmnimaAPIWaitForPrompt(pio, 1))
    {
	    retVal=OmnimaAPISendCmdArp2(pio, waitForResp, pRespVal, cmd, arp);
    }
	
	va_end(arp);
    
    return retVal;
}

unsigned int OmnimaAPISendCmdAndGetValueResponse(const termioType *pio,
                                                 unsigned int *pResVal,
												const char *cmd,
                                                 ...)
//Send a command, waits for response, returns response
{
    unsigned int i, retVal;
    
    va_list arp;
    va_start(arp, cmd);
    
    for(i=0;!(retVal=OmnimaAPISendCmdArp2(pio, 1, pResVal, cmd, arp)) && i<10;i++)
    {
        DebugPrintf(1, "Waiting for response to %s\n", cmd);
    }
    va_end(arp);
    if (i==10) return 0; else return retVal;
}

unsigned int OmnimaAPIPrepareFileForUpload(const char *fileToUpload,
                                  unsigned char **ppBuff,
                                  unsigned int *pBuffLen,
                                  unsigned int *pChecksum,
                                  unsigned char bPrintHexFile)
//Loads data file to memory (this progam memory)
{
    unsigned int fl, i;
    unsigned char b,*buff=0;
    int pflashFile=-1;
    unsigned int checksum;
  
    fl=0;
    pflashFile = open(fileToUpload, O_RDONLY | O_BINARY);
    if (pflashFile==-1)
    {
            DebugPrintf(1, "Can't open %s\n", fileToUpload);
            return 0;
    }

    fl=lseek(pflashFile, 0L, 2);
    lseek(pflashFile, 0L, 0);

    if (fl>0)
    {
        buff=malloc(fl);
        read(pflashFile, buff, fl);
    }
    close(pflashFile);  
    
    for(checksum=0xaa,i=0;i<fl;i++) 
    {
        b=buff[i];
        checksum+=b;

        if (bPrintHexFile)
        {
            //Print out bytes to stdio
            if (i%0x10==0) DebugPrintf(1, "\n%04x:%04x ",checksum, i);
            DebugPrintf(1, "%02x ", b);
        }
    }
	    
    if (bPrintHexFile)
    {
        DebugPrintf(1, "\nFile size: %d, checksum; %x\n", fl, checksum);
    }
    
    if (ppBuff) *ppBuff=buff; else free(buff);
    if (pBuffLen) *pBuffLen=fl;
    if (pChecksum) *pChecksum=checksum;
    return 1;
}

unsigned int OmnimaAPIReadFromSSD(const termioType *pio, 
                                  unsigned int startAddr,
                                  unsigned int len)
{
    return OmnimaAPISendCmd(pio, 0, 0, "#@SSDMEMRW %d %d\n", (int)startAddr, (int)len);
}

unsigned int OmnimaAPISendFileToSSD(const termioType *pio, 
                           const char *fileToUpload,
                           int startAddr,
                           int inclCheck)
//Uploads binary file from host to SSD video memory                           
{
    unsigned int fl, buffSize;
    unsigned char *buff=0, *filebuff=0;
    unsigned int checksum;
    
    if (!OmnimaAPIPrepareFileForUpload(fileToUpload, &filebuff, &fl, &checksum, 0))
    {
        DebugPrintf(1, "Failed to open file: %s\n", fileToUpload);
        return 0;
    }

    if (inclCheck)
	{
		buffSize=fl+8;
	} else 
	{
		buffSize=fl;
	}

    buff=malloc(buffSize);
    memcpy(inclCheck?&buff[8]:&buff[0], filebuff, fl);

    if (inclCheck)
    {
        buff[0]=((unsigned char*)&fl)[0];
        buff[1]=((unsigned char*)&fl)[1];
        buff[2]=((unsigned char*)&fl)[2];
        buff[3]=((unsigned char*)&fl)[3];
        buff[4]=((unsigned char*)&checksum)[0];
        buff[5]=((unsigned char*)&checksum)[1];
        buff[6]=((unsigned char*)&checksum)[2];
        buff[7]=((unsigned char*)&checksum)[3];
        
        DebugPrintf(3, "File checksum is %08x\n", checksum);
        DebugPrintf(3, "File size is %08x\n", fl);
    }

	DebugPrintf(3, "Sending SSDMEMWW command\n", fl);
    OmnimaAPISendCmd(pio, 0, 0, "#@SSDMEMWW %d %d BI\n", (int)startAddr, (int)buffSize);

#if defined(SLOWERTRANSFER)
    for(fl=0;fl<buffSize;fl++)
    {
		DebugPrintf(3, ".");
        OSendComPortBlock(pio, &buff[fl], 1);
		
		//Sourceforge PL2303 driver on OS X can't support bulk transfer at 230400bps, slow down a bit
        for(pp=0;pp<40000;pp++);
    }
    OClearSerialPortBuffers(pio);
#else
	DebugPrintf(3, ".");
    OSendComPortBlock(pio, buff, buffSize);
    OClearSerialPortBuffers(pio);
#endif

	DebugPrintf(3, "\nWaiting for LCD to indicate SSDMEMWW command completion\n");
    OmnimaAPIWaitForPrompt(pio, 0);
	

    if (buff) free(buff);
    if (filebuff) free(filebuff);
    return 1;
}

unsigned int OmnimaAPIStreamMJPEG(const termioType *pio, 
                                  const char *fileToUpload)
//Streams JPEG/MJPEG file to SSD
{
    unsigned int pflashFile=-1, rb;
    unsigned char smBuf[64*1024];
    unsigned int p,fr;
    
    unsigned int realsize;
    char AnswerBuffer[64*1024];
    const unsigned int AnswerLength = sizeof(AnswerBuffer);
      
    pflashFile = open(fileToUpload, O_RDONLY | O_BINARY);
    if (pflashFile==-1)
    {
            DebugPrintf(1, "Can't open %s\n", fileToUpload);
            return 0;
    }
	
	//Step over the first 512 bytes of MJPEG header stuff
	//read(pflashFile, smBuf, 512);
    
    OmnimaAPISendCmd(pio, 0, 0, "#@SMJPEG\n");
    Sleep(1);

    for(fr=0,p=0;(rb=read(pflashFile, smBuf, 12*1024))>0;p+=rb,fr++)
    {
        DebugPrintf(1, "Buffer %d - sending %d bytes\n", fr, rb);
        
        OSendComPortBlock(pio, smBuf, rb);
        OClearSerialPortBuffers(pio);
            
        //OReceiveComPort(pio, AnswerBuffer, AnswerLength - 1, &realsize, 0, 100);
        //if(realsize>0) DebugPrintf(1, "Rec: %s", AnswerBuffer);
    }
    
    close(pflashFile);
    OmnimaAPIWaitForPrompt(pio, 0);

    return 1;
}

extern int debug_level;

unsigned int OmnimaAPIProgramFlash(const termioType *pio, const char *fileToUpload)
//Uploads data to Omnima LCD external flash
{
    unsigned int currentChecksum, fileChecksum, writeResult=0xff;

    OmnimaAPIWaitForPrompt(pio, 1);
    Sleep(2000);
    OClearSerialPortBuffers(pio);    
    
    //Turn Echo Off
    if (!OmnimaAPISendCmd(pio, 1, 0, "#@Echo Off\n\r"))
    {
        DebugPrintf(1, "No response from #@Echo Off\n");
        return 0;
    }
    
    if (!OmnimaAPIPrepareFileForUpload(fileToUpload, 0, 0, &fileChecksum, 0))
    {
        DebugPrintf(1, "Failed to read flash data file: %s\n", fileToUpload);
        return 0;
    }
    
    OmnimaAPIWaitForPrompt(pio, 1);
    Sleep(2000);
    OClearSerialPortBuffers(pio);

    //Get checksum from EE flash
    if (!OmnimaAPISendCmdAndGetValueResponse(pio, &currentChecksum, "#@WEE 2\n\r"))
    {
        DebugPrintf(1, "No response from #@WEE 2\n");
        return 0;
    }
        
    if (currentChecksum==fileChecksum)
    {
        DebugPrintf(1, "Local data checksum: %08x\n", fileChecksum);
        DebugPrintf(1, "OLCD data checksum: %08x\n", currentChecksum);
        DebugPrintf(1, "Flash data is up-to-date\n\n");
        return 1;
    }
	
    OmnimaAPIWaitForPrompt(pio, 1);
    Sleep(2000);
    OClearSerialPortBuffers(pio);
    
    DebugPrintf(1, "Uploading flash data from %s\nChecksum new:%x != old:%x\n", fileToUpload, fileChecksum, currentChecksum);
    
    if (!OmnimaAPISendFileToSSD(pio, fileToUpload, 0, 1))
    {
        DebugPrintf(1, "Failed to copy data to LCD SSD memory\n");
        return 0;     
    }
    
    DebugPrintf(1, "Flash data uploaded to LCD, run #@WEE 1 to commit the changes to the flash\n");
    
    //Turn Echo On
    if (!OmnimaAPISendCmd(pio, 1, 0, "#@Echo On\n"))
    {
        DebugPrintf(1, "No response from #@Echo On\n");
        return 0;
    }
    
    return 1;
                
    //Write data to EE flash, do not timeout quickly, this can be a lenghty procedure
    OmnimaAPISetSerialTotalTimeout(0);
    OmnimaAPISetPrintResponses(1);
    
    if (!OmnimaAPISendCmd(pio, 1, &writeResult, "#@WEE 1\n"))
    {
        DebugPrintf(1, "No response from #@WEE 1, retVal=%02x\n", writeResult);
        return 0;
    }
    
    //Restore default timeout
    OmnimaAPISetSerialTotalTimeoutDefault();
    OmnimaAPISetPrintResponses(0);
    
    Sleep(2000);
    OClearSerialPortBuffers(pio);
    OmnimaAPIWaitForPrompt(pio, 0);
    
    //Get checksum from EE flash
    OmnimaAPISetPrintResponses(1);
    if (!OmnimaAPISendCmd(pio, 1, &currentChecksum, "#@WEE 2\n"))
    {
        DebugPrintf(1, "No response from #@WEE 2-2\n");
        return 0;
    }
    
    OmnimaAPISetPrintResponses(0);
    
    //Turn Echo On
    if (!OmnimaAPISendCmd(pio, 1, 0, "#@Echo On\n\r"))
    {
        DebugPrintf(1, "No response from #@Echo On\n");
        return 0;
    }    

    if (currentChecksum==fileChecksum)
    {
        DebugPrintf(1, "%s data file uploaded to EE flash\n", fileToUpload);
        return 1;
    } else
    {
        DebugPrintf(1, "Failed to upload config data to EE flash\n");
        return 0;
    }

    return 1;
}

unsigned int OmnimaAPITestPort(const char *port, const char *baud)
//Test port/baud comunication to Omnima LCD 
{    
    termioType io;
    int retVal;
    
    if (!OOpenSerialPort(&io, port, baud)) return 0;
    
    OmnimaAPISetSerialSilenceTimeout(500);
    OmnimaAPISetSerialTotalTimeout(500);

    retVal = OmnimaAPIWaitForPrompt(&io, 1);
    
    OmnimaAPISetSerialSilenceTimeoutDefault();
    OmnimaAPISetSerialTotalTimeoutDefault();
    
    OClearSerialPortBuffers(&io);
    OCloseSerialPort(&io);
    return retVal;
}

unsigned int OmnimaAPIEnterISP(const termioType *pio, unsigned char bReboot)
{
    int retryVal;
    
    //Wait for quite some time for the API to respond
    for(retryVal=0;retryVal<20;retryVal++)
    {
      if (OmnimaAPIWaitForPrompt(pio, 1)) break;
    }
    if (retryVal==20) return 0;
    
    Sleep(2000);
    OClearSerialPortBuffers(pio);
	
    if (bReboot)
        OmnimaAPISendCmd(pio, 0, 0, "#@EnterISP 0xbb\n");
    else
        OmnimaAPISendCmd(pio, 0, 0, "#@EnterISP\n");
    Sleep(500);
	
    return 1;
}

//Serial API implementation
unsigned int OmnimaAPIFileToSSD(const termioType *pio, unsigned int addr, const char *fileName)
//Load file from SD card to SSD video memory using fast bulk transfer
{
    return OmnimaAPISendCmdWithSync(pio, 1, 0, "#@FileToSSD %d %s\n", addr, fileName); 
}

const char *g_WindowStrType[WIN_MAX] =
{
    "MW", //Main Window
    "",
    "",
    "FW", //Floating window
    "CR1", //Cursor 1
    "CR2", //Cursor 2
};

unsigned int OmnimaAPIBitBlt(const termioType *pio, 
                             WINDOWTYPE srcWin,
                             unsigned int x1, unsigned int y1,
                             unsigned int w1, unsigned int h1,
                             WINDOWTYPE dstWin,
                             unsigned int x2, unsigned int y2,
                             unsigned int w2, unsigned int h2)
{
    return OmnimaAPISendCmdWithSync(pio, 1, 0, "#@Blt %s %d %d %d %d %s %d %d %d %d\n", 
                                    g_WindowStrType[srcWin], x1, y1, w1, h1,
                                    g_WindowStrType[dstWin], x2, y2, w2, h2); 
}

unsigned int OmnimaAPILine(const termioType *pio, 
                           WINDOWTYPE dstWin,
                           unsigned int x1, unsigned int y1,
                           unsigned int x2, unsigned int y2)
{
    return OmnimaAPISendCmdWithSync(pio, 1, 0, "#@Line %s %d %d %d %d\n", 
                                    g_WindowStrType[dstWin], x1, y1, x2, y2); 
}

const char* GetRotateStr(ROTATETYPE r)
{
    switch (r)
    {
      case VFLIP:
        return "H2";
      case ROT270:
        return "Ver";
      case ROT90:
        return "V2";
      default:        
        return "Hor";
    }
}

unsigned int OmnimaAPITextOut(const termioType *pio, 
                             WINDOWTYPE dstWin,
                             ROTATETYPE r,
                             unsigned int x1, unsigned int y1,
                             const char *str)  
{
    return OmnimaAPISendCmdWithSync(pio, 1, 0, "#@Text %s %s %d %d %s\n", 
                                    g_WindowStrType[dstWin],
                                    GetRotateStr(r),
                                    x1, y1, str); 
}

